package ru.fadeev.crammer.ui.window;

import java.awt.*;
import java.awt.event.*;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Vector;
import java.util.Random;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;

import ru.fadeev.crammer.DatabaseManager;
import ru.fadeev.crammer.GFIndicesStorage;
import ru.fadeev.crammer.Settings;
import ru.fadeev.crammer.TestManager;
import ru.fadeev.crammer.Utils;
import ru.fadeev.crammer.data.DictionariesManager;
import ru.fadeev.crammer.data.WordRecord;
import ru.fadeev.crammer.data.ThroughWordIndex;
import ru.fadeev.crammer.ui.ArrowOptionPane;
import ru.fadeev.crammer.ui.BasicCellRenderer;


/**
 * TestGapsFillingDialog class provided GUI for Gaps Filling test.
 * @author Konstantin Fadeyev
 *
 */
public class TestGapsFillingDialog extends JDialog {	
	// Constants
	private final static Color WRONG_COLOR = new Color(255, 210, 210);
	private final static Color WRONG_SEL_COLOR = new Color(245, 200, 200);
	private final static Color RIGHT_COLOR = new Color(210,255,210);
	private final static Color RIGHT_SEL_COLOR = new Color(200,245,200);
	private final static Color SELECTION_COLOR = new Color(220,220,255);

	// UI Components
	private JPanel mainPanel;
	private JPanel topPanel;
	
	private JScrollPane phraseScrollPane;
	private JTable phrasesTable;
    private TestGFPhraseTableModel phrasesTableModel;    

	private JPanel wordsPanel;
	private JPanel matchButtonPanel;
	private JButton matchButton;
	private JScrollPane wordsScrollPane;
	private JTable wordsTable;
    private TestGFWordTableModel wordsTableModel;
    
    private JPanel bottomPanel;
    
    private JPanel remainPanel;
    private JLabel remainLabel;

    private JLabel losersLabel;
  	private JButton showLosersButton;
    
    private JPanel buttonsPanel;   
  	private JButton terminateButton;
  	private JButton nextButton;
	
  	
    // User fields 
  	
  	/**
	 * Vector containing currently shown words and related information
	 */
  	private Vector<TestGFRecord> currentWords; 
	private DictionariesManager dictionariesManager;
	
  	/** 
  	 * Vector containing all words that have phrase containing them 
  	 * for Gaps Filling Test 
  	 */
  	private Vector<TestGFHeapRecord> heap;    

  	/** 
  	 * Vector containing all words 
  	 * that don't have phrase containing them 
  	 */
  	private Vector<TestGFHeapRecord> losers;    
  	
    private Random random;
  	
  	/**
  	 * Vector containing indices of currentWords records.
  	 * This vector is generated randomly and used to show words in shuffled order
  	 */
  	private Vector<Integer> shuffledIndices;
	private GFIndicesStorage storage;
  	
       	
	/**
	 * Constructs TestGapsFillingDialog and prepares data for test
	 * @param owner parent window
	 * @param dictionariesManager object of DictionariesManager
	 * @param storage object of GFIndicesStorage
	 */
    public TestGapsFillingDialog(JFrame owner, 
    		DictionariesManager dictionariesManager, GFIndicesStorage storage) {
    	super(owner,true);    
    	
    	this.storage = storage;
    	this.dictionariesManager = dictionariesManager;
    	
    	heap = new Vector<TestGFHeapRecord>();
    	losers = new Vector<TestGFHeapRecord>();
        currentWords = new Vector<TestGFRecord>();
        shuffledIndices = new Vector<Integer>();
    	random = new Random(System.currentTimeMillis());
        
        initComponents();
        placeComponents();
        
        // Setting up window size. Initial window height is set depending 
        // on shown table rows number. 
        int rowsNumber = Settings.getInt("gapsFillingNumberOfLines");
        int windowHeight = 97 + rowsNumber * wordsTable.getRowHeight();
        
    	int x = Settings.getInt("testGapsFillingDialogX");
    	int y = Settings.getInt("testGapsFillingDialogY");
    	int w = Settings.getInt("testGapsFillingDialogW");
        if(x<0 || y<0 || w<0) {
        	Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
            setSize(screenSize.width * 9/10, windowHeight);
            setLocation(screenSize.width/20, (screenSize.height - windowHeight)/3);
        } else {
        	setSize(w, windowHeight);
        	setLocation(x, y);        	
        }
   
        setTitle("Gaps Filling");
        
        fillHeap();
        nextAction();
        refreshMatchButton();
    }
    
    
    /**
     * Refreshes losers label and button depending on losers array size 
     */
    private void checkForLosers() {
    	if(losers.size() > 0) {
    		if(losers.size() == 1 ) {
    			losersLabel.setText("There is 1 word that has no phrase " +
    					"in phrase corpus");
    		} else {
    			losersLabel.setText("There is " + losers.size() + 
    					" words that has no phrase in phrase corpus");
    		}
    		losersLabel.setVisible(true);
    		showLosersButton.setVisible(true);
    	} else {
    		losersLabel.setVisible(false);
    		showLosersButton.setVisible(false);
    	}   	
    }
    
    
    /**
     * Initializes UI Components of dialog
     */
    private void initComponents() {
        mainPanel = new JPanel();
    	topPanel = new JPanel();
        
        // Tables
    	wordsTableModel = new TestGFWordTableModel();    
    	wordsTable = new JTable(wordsTableModel);
    	wordsTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
    	wordsTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

    	wordsTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    	wordsTable.getTableHeader().setReorderingAllowed(false);    
    	
    	ListSelectionListener selectionListener = new ListSelectionListener() {
	    	public void valueChanged(ListSelectionEvent e) {
	    		refreshMatchButton();
	    	}
    	};
    	       
    	wordsTable.getSelectionModel().addListSelectionListener(selectionListener);
    	wordsTable.getColumnModel().getSelectionModel()
				.addListSelectionListener(selectionListener);

    	BasicCellRenderer wordCellRenderer = 
    		new BasicCellRenderer();
    	wordsTable.setDefaultRenderer(String.class, wordCellRenderer);
    	wordsTable.setSelectionForeground(Color.BLACK);
    	
    	wordsTable.addMouseListener(
				new MouseAdapter() {
					public void mouseClicked(MouseEvent ev) {						
						if(ev.getButton() == MouseEvent.BUTTON1 && ev.getClickCount() == 2) {
							matchAction();
						}
					}
				});


    	wordsScrollPane = new JScrollPane(wordsTable);
    	
    	wordsPanel = new JPanel();
    	
    	phrasesTableModel = new TestGFPhraseTableModel();    
    	phrasesTable = new JTable(phrasesTableModel);
    	phrasesTable.getSelectionModel().setSelectionMode(
    			ListSelectionModel.SINGLE_SELECTION);
    	phrasesTable.getTableHeader().setReorderingAllowed(false);    	

    	TestGFTableCellRenderer phraseCellRenderer = 
    		new TestGFTableCellRenderer();
    	phrasesTable.setDefaultRenderer(String.class, phraseCellRenderer);
    	phrasesTable.getSelectionModel()
    			.addListSelectionListener(selectionListener);
    	phrasesTable.getColumnModel().getSelectionModel()
				.addListSelectionListener(selectionListener);

        TestGFCellEditor testEditor = new TestGFCellEditor();                
        phrasesTable.setDefaultEditor(String.class, testEditor);
        
    	phraseScrollPane = new JScrollPane(phrasesTable);

    	matchButtonPanel = new JPanel();
		ImageIcon matchIcon = new ImageIcon("icons/16/match.png");
		matchButton = new JButton(matchIcon);
		matchButton.setRolloverIcon(matchIcon);
		matchButton.setToolTipText("Match selected word with selected phrase");

    	matchButton.addActionListener(
			    new AbstractAction() 
			    {
			        public void actionPerformed(ActionEvent ev)
			        {
			        	matchAction();
			        }
			    });
    	
        // Bottom panel and buttons
        bottomPanel = new JPanel();
        
        remainPanel = new JPanel();
        remainLabel = new  JLabel();
        
        losersLabel = new  JLabel();
        Color losersColor = new Color(150, 50, 0);
        losersLabel.setForeground(losersColor);
        
      	showLosersButton = new JButton("View words");
      	showLosersButton.setForeground(losersColor);
      	showLosersButton.addActionListener(
			    new AbstractAction() 
			    {
			        public void actionPerformed(ActionEvent ev)
			        {
			        	showLosers();
			        }
			    });

        buttonsPanel = new JPanel();  
      	terminateButton = new JButton("Terminate");
      	terminateButton.addActionListener(
			    new AbstractAction() 
			    {
			        public void actionPerformed(ActionEvent ev)
			        {
			        	terminateAction();
			        }
			    });
      	
      	nextButton = new JButton("Next");
      	nextButton.addActionListener(
			    new AbstractAction() 
			    {
			        public void actionPerformed(ActionEvent ev)
			        {
			        	nextAction();
			        }
			    });
      	        
        setContentPane(mainPanel);
    }

    
    /**
     * Searches for phrase in database for every word selected for test.
     * If search is successful word with the phrase is placed to heap array
     * for further testing. If no phrase for word was found, word is placed to
     * losers array. 
     */
    public void fillHeap() {
        heap.removeAllElements();
        losers.removeAllElements();
        try {			   
	        Connection connection = DatabaseManager.getConnection();
	        dictionariesManager.startTestWordsIteration();
			while(dictionariesManager.hasNextTestWord(TestManager.GAPS_FILLING)) {
				WordRecord wordRecord = dictionariesManager.getNextTestWord();		
            	TestGFHeapRecord newHeapRecord = 
            		new TestGFHeapRecord(wordRecord);
            	getPhrase(newHeapRecord, connection);               	
            	newHeapRecord.wordIndex = 
            		dictionariesManager.getCurrentIndex();;
            	if(newHeapRecord.phrase == null) {
            		losers.addElement(newHeapRecord);
            	} else {		                	
            		heap.addElement(newHeapRecord);
            	}           	
			}
			connection.close();
    	} catch(Exception e) {
    		e.printStackTrace();
    	}	
    }
    
    
    /**
     * Gets phrase that contains word specified by newRecord.word from database.
     * Result phrase is stored in newRecord.phrase
     * @param newHeapRecord record initialized with some word
     * @param connection opened database connection
     */
    private void getPhrase(TestGFHeapRecord newHeapRecord, 
    		Connection connection) {
    	try {
    		newHeapRecord.phraseId = 
    			storage.getPhraseId(newHeapRecord.word.getId(), 
    				connection);
    		if(newHeapRecord.phraseId >= 0) {
    			Statement statement = connection.createStatement();
				ResultSet resultSet = statement.executeQuery(
						"SELECT PHRASE_TEXT " +
						"FROM PHRASE " +
						"WHERE ID = " + newHeapRecord.phraseId);
				resultSet.next();
				newHeapRecord.phrase = resultSet.getString(1);
    			statement.close();
    		} else {
    			newHeapRecord.phrase = null;
    		}
    	} catch(Exception e) {
    		e.printStackTrace();
    	}
    }
    
    
    /**
     * Checks if word heap has another word
     * @return true if word heap has another word, false otherwise
     */
    private boolean hasNextWord() {
		if(heap.size()==0) {
			random.setSeed(System.currentTimeMillis());
		}
		return heap.size()!=0;
	}
    
    
    /**
     * Main test function. Matches a phrase specified by phraseIndex and a word
     * specifyid by wordIndex.
     * @param phraseIndex index of phrase to match
     * @param wordIndex index of word to match
     * @return true, if phrase and word matches, i.e. test is ok.
     */
    public boolean match(int phraseIndex, int wordIndex) {
    	Integer shuffledWordIndex = shuffledIndices.elementAt(wordIndex);
    	TestGFRecord gfWordRecord = currentWords.elementAt(shuffledWordIndex.intValue());
    	WordRecord wordRecord = gfWordRecord.wordRecord;
    	
    	TestGFRecord gfPhraseRecord = currentWords.elementAt(phraseIndex);
    	int previousCheckResult = gfPhraseRecord.checkResult;
    	boolean successful = false;
    	
		if(previousCheckResult == TestGFRecord.RIGHT) {
			return true;
		}
    	if (wordRecord.getWord().equals(gfPhraseRecord.wordRecord.getWord())) {
    			shuffledIndices.remove(wordIndex);
    		gfPhraseRecord.checkResult = TestGFRecord.RIGHT; 
    		successful = true;
    	} else {
    		gfPhraseRecord.checkResult = TestGFRecord.WRONG;    		
    	}
    	
		if(previousCheckResult == TestGFRecord.UNKNOWN) {
			gfPhraseRecord.wordRecord.addHistoryRecord(TestManager.GAPS_FILLING, 
					successful);
			gfPhraseRecord.wordRecord.refreshNeededToTest(TestManager.GAPS_FILLING, 
					gfPhraseRecord.wordIndex.getDictIndex(), 
					gfPhraseRecord.wordIndex.getSheetIndex());
			storage.incrementTestCount(gfPhraseRecord.wordRecord.getId(), 
					gfPhraseRecord.phraseId);
		}
    	return successful;
    }
    
    
    /**
     * Match button action handler.
     */
    private void matchAction() {
    	Utils.finishTableEditing(phrasesTable);
    	int wordSelectedIndex = wordsTable.getSelectedRow();
    	int phraseSelectedIndex = phrasesTable.getSelectedRow();
    	int phraseIndexToSelect = -1;
    	int wordIndexToSelect = -1;

    	if(match(phraseSelectedIndex, wordSelectedIndex)) {
			if(phraseSelectedIndex != currentWords.size() - 1) {
				phraseIndexToSelect = phraseSelectedIndex + 1;	
			} else {
				phraseIndexToSelect = 0;
			}
			wordIndexToSelect = wordSelectedIndex;
			if(wordSelectedIndex == shuffledIndices.size()) {
				wordIndexToSelect = wordIndexToSelect - 1;
			}
    	} else {
			phraseIndexToSelect = phraseSelectedIndex;
			if(wordSelectedIndex != shuffledIndices.size() - 1) {
				wordIndexToSelect = wordSelectedIndex + 1;
			} else {
				wordIndexToSelect = 0;
			}
    	}
    	
    	phrasesTableModel.fireTableDataChanged();
    	wordsTableModel.fireTableDataChanged();
    	
    	if (phraseIndexToSelect >=0) {
    		phrasesTable.setRowSelectionInterval(phraseIndexToSelect, phraseIndexToSelect);
    		phrasesTable.setColumnSelectionInterval(0, 0);
    	}
    	if (wordIndexToSelect >=0) {
    		wordsTable.setRowSelectionInterval(wordIndexToSelect, wordIndexToSelect);
    		wordsTable.setColumnSelectionInterval(0, 0);
    	}
    }
    
    
    /**
	 * Gets from the heap next portion of records for test (randomly). Fills
	 * phrase and word tables. 
	 */
    private void nextAction() {
    	if (thereIsUncheckedWords()) {
            int n = ArrowOptionPane.showConfirmDialog(this,"There are some nontested words.\n" +
                    "Do you really want to move on?", "Warning", JOptionPane.YES_NO_OPTION);
            if(n != JOptionPane.OK_OPTION) {
            	return;
            }    		
    	}
    	
    	Utils.finishTableEditing(phrasesTable);
    	
    	currentWords.removeAllElements();
		for(int i=0; i < Settings.getInt("gapsFillingNumberOfLines"); i++) {
			if(!hasNextWord())
				break;
			int pos = random.nextInt(heap.size());
			TestGFHeapRecord heapRecord = (TestGFHeapRecord)heap.elementAt(pos);
            heap.remove(pos);
            
            TestGFRecord testRecord = new TestGFRecord(heapRecord);
            testRecord.dottedPhrase = Utils.markWord(testRecord.phrase, 
            		testRecord.wordRecord.getWord(), Utils.DOT_MARK);
            testRecord.boldPhrase = "<html>" + Utils.markWord(testRecord.phrase, 
            		testRecord.wordRecord.getWord(), Utils.BOLD_MARK) + "</html>";
            currentWords.addElement(testRecord);
		}
		
		Vector<Integer> shuffleIndicesTemp = new Vector<Integer>();		
		for(int i=0; i< currentWords.size(); i++) {
			shuffleIndicesTemp.addElement(new Integer(i));
		}
		
		shuffledIndices.removeAllElements();
		for(int i=0; i< currentWords.size(); i++) {
			int pos = random.nextInt(shuffleIndicesTemp.size());
			shuffledIndices.addElement(shuffleIndicesTemp.elementAt(pos));
			shuffleIndicesTemp.remove(pos);
		}		
		
		phrasesTableModel.fireTableDataChanged();
		wordsTableModel.fireTableDataChanged();
		remainLabel.setText("Remain: " + heap.size());
		nextButton.setEnabled(heap.size() > 0);
		
		if(currentWords.size() > 0) {
			phrasesTable.setRowSelectionInterval(0, 0);
			phrasesTable.setColumnSelectionInterval(0, 0);
			wordsTable.setRowSelectionInterval(0, 0);
			wordsTable.setColumnSelectionInterval(0, 0);
		}
		
		checkForLosers();
	}
    
    
    /**
     * Places UI components using different layout managers
     */
    private void placeComponents() {
    	mainPanel.setLayout(new BorderLayout());
    	mainPanel.add(topPanel, BorderLayout.CENTER);
    	mainPanel.add(bottomPanel, BorderLayout.SOUTH);
    	
    	topPanel.setLayout(new BorderLayout());
    	topPanel.add(phraseScrollPane, BorderLayout.CENTER);
    	topPanel.add(wordsPanel, BorderLayout.EAST);
    	
    	wordsPanel.setPreferredSize(new Dimension(198, 200));
    	wordsPanel.setLayout(new BorderLayout());
    	wordsPanel.add(matchButtonPanel, BorderLayout.WEST);
    	wordsPanel.add(wordsScrollPane, BorderLayout.CENTER);
    	
    	matchButtonPanel.setPreferredSize(new Dimension(30, 30));  	
    	matchButtonPanel.setLayout(new BorderLayout());
    	Box box = new Box(BoxLayout.X_AXIS);    	
    	matchButtonPanel.add(box);
    	box.add(Box.createRigidArea(new Dimension(3, 0)));   	
    	box.add(matchButton);
    	box.add(Box.createRigidArea(new Dimension(3, 0)));
    	matchButton.setPreferredSize(new Dimension(20, 20));
    	
    	
    	bottomPanel.setPreferredSize(new Dimension(200, 40));
    	bottomPanel.setLayout(new BorderLayout());
    	bottomPanel.add(remainPanel, BorderLayout.WEST);
    	bottomPanel.add(buttonsPanel, BorderLayout.EAST);  	
    	
    	remainPanel.setPreferredSize(new Dimension(100, 40));
    	remainPanel.setLayout(null);
    	remainPanel.add(remainLabel);
    	
    	remainLabel.setBounds(10, 10, 70, 20);
    	     	
    	buttonsPanel.setPreferredSize(new Dimension(590, 40));
    	buttonsPanel.setLayout(null);
    	buttonsPanel.add(losersLabel);
    	buttonsPanel.add(showLosersButton);
    	buttonsPanel.add(nextButton);
    	buttonsPanel.add(terminateButton);
    	
        losersLabel.setBounds(0, 10, 270, 20);
        showLosersButton.setBounds(270, 10, 90, 20);
    	nextButton.setBounds(390, 10, 90, 20);
    	terminateButton.setBounds(490, 10, 90, 20);
    }
    
    
  	/**
  	 * Enable/disable match button depending on selected records in tables.
  	 */
    private void refreshMatchButton() {
    	int wordSelected = wordsTable.getSelectedRow();
    	int phraseSelected = phrasesTable.getSelectedRow();
    	if (wordSelected < 0 || phraseSelected < 0) {
    		matchButton.setEnabled(false);
    	} else {
    		matchButton.setEnabled(true);
    	}
    }
    
    
    /**
     * Overwritten, so it saves window location when dialog is closed
     */
    public void setVisible(boolean v) {
    	if(v == false) {
	    	Settings.setInt("testGapsFillingDialogX", getX());
	    	Settings.setInt("testGapsFillingDialogY", getY());
	    	Settings.setInt("testGapsFillingDialogW", getWidth());
    	}
    	super.setVisible(v);
    }
    
    
    /**
     * Launches ViewLosersDialog that show losers (words that doesn't have phrase
     * containing them)
     */
    private void showLosers() {
    	ViewLosersDialog dialog = new ViewLosersDialog(this, losers, storage);
    	dialog.setVisible(true);
    }
    
    
    /**
     * Terminate button action handler. Finishes test.
     */
    private void terminateAction() {
    	if (thereIsUncheckedWords()) {
            int n = ArrowOptionPane.showConfirmDialog(this,
            		"There are some nontested words.\n" +
                    "Do you really want to end test?", "Warning", 
                    JOptionPane.YES_NO_OPTION);
            if(n != JOptionPane.OK_OPTION) {
            	return;
            }    		
    	}
    	
    	setVisible(false);
    }
    
    
    /**
     * Checks if current words array contains non-checked words
     * @return true, if current words array contains non-checked words
     */
    public boolean thereIsUncheckedWords() {
        for(int i = 0; i < currentWords.size(); i++) {
            TestGFRecord record = (TestGFRecord)currentWords.elementAt(i);
            if(record.checkResult == TestGFRecord.UNKNOWN) {
            	return true;
            }
        }
        return false;
    }
    
    
    /**
     * TestGFCellEditor class represents cell editor for words table 
     * in TestGapsFillingDialog
     * @author Konstantin Fadeyev
     */
    private class TestGFCellEditor extends AbstractCellEditor 
			implements TableCellEditor {
        private JTextPane textPane;
        private Font defaultFont;

        public TestGFCellEditor() {
    		textPane = new JTextPane();
    		textPane.setEditable(false);
    		textPane.setContentType("text/html");
    		textPane.setMargin(new Insets(0,0,0,0));
            JTextField doomy = new JTextField();
            defaultFont = doomy.getFont();
        }
        
        public Object getCellEditorValue() {
        	return textPane.getText();
        }
        
        public Component getTableCellEditorComponent(JTable table,Object value,
        						boolean isSelected, int row, int column) {
        	textPane.setText(
        			"<html>" +
        				"<head>" +
        					"<style> " +
        						"body { " +
        							"font-family: '"+ defaultFont.getFamily() +"';" +  
        							"font-size:" + defaultFont.getSize() + "pt;" +
        						"}" +
        					"</style>" +
        				"</head>" +
        				"<body>" +
        					"<nobr>" + 
        						(String)value +
        					"</nobr>" +
        			    "</body>" +
        			"</html>"
        			);
        	
            TestGFRecord record = currentWords.elementAt(row);
        	switch(record.checkResult) {
            case TestGFRecord.UNKNOWN:
            	textPane.setBackground(SELECTION_COLOR);
                break;
            case TestGFRecord.RIGHT:
            	textPane.setBackground(RIGHT_SEL_COLOR);
                break;
            case TestGFRecord.WRONG:
            	textPane.setBackground(WRONG_SEL_COLOR);
            }
            return textPane;
        }
    }
    
    
    /**
     * TestGFHeapRecord class is used for preliminary collecting words 
     * for Gaps Filling Test 
     * @author Konstantin Fadeyev
     *
     */
    public class TestGFHeapRecord {
    	public WordRecord word;
    	
    	/*
    	 * Index of word to keep track of word's place in dictionary
    	 */
    	public ThroughWordIndex wordIndex; 
    	public String phrase;
    	public int phraseId;
    	
    	public TestGFHeapRecord(WordRecord word) {
    		this.word = word;
    	}
    }
    
    
    /**
     * TestGFPhraseTableModel class represents table model
     * for phrases table in TestGapsFillingDialog
     * @author Konstantin Fadeyev
     *
     */
    private class TestGFPhraseTableModel extends AbstractTableModel {
    	public TestGFPhraseTableModel() {
    		super();
    	}

    	public Class<?> getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

    	public int getColumnCount() {
    		return 1;
    	}

    	public String getColumnName(int num) {
    		return "Phrase";
    	}

        public int getRowCount() {
    	    return currentWords.size();
    	}

    	public Object getValueAt(int row, int column) {
    		if(row < currentWords.size()) {
    			TestGFRecord record = currentWords.elementAt(row);
    			if (record.checkResult == TestGFRecord.RIGHT) {
    				return record.boldPhrase;
    			} else {
    				return record.dottedPhrase;
    			}
    		}
    		else {
    			return "";
    		}
    	}

        public boolean isCellEditable(int rowIndex, int columnIndex) {
    		return true;
        }
    }
  
    
    /**
     * TestGFRecord class is used for keeping word records 
     * during Gaps Filling Test
     * @author Konstantin Fadeyev
     *
     */
    private class TestGFRecord {
    	public WordRecord wordRecord;
    	
    	/**
    	 * Index of word to keep track of word's place in dictionary
    	 */
    	public ThroughWordIndex wordIndex; 
    	public String phrase;
    	public int phraseId;
    	public String dottedPhrase;
    	public String boldPhrase;

    	public int checkResult;   
    	
    	public static final int UNKNOWN = 0;
    	public static final int RIGHT = 1;
    	public static final int WRONG = 2;
    	
    	public TestGFRecord(TestGFHeapRecord heapRecord) {
    		this.wordRecord = heapRecord.word;
    		this.wordIndex = heapRecord.wordIndex;
    		this.phrase = heapRecord.phrase;
    		this.phraseId = heapRecord.phraseId;
    		checkResult = UNKNOWN;
    	}
    }
    

    /**
     * TestGFTableCellRenderer class represents cell renderer 
     * for phrase table in TestGapsFillingDialog
     * @author Konstantin Fadeyev
     *
     */
    private class TestGFTableCellRenderer extends DefaultTableCellRenderer {      
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                                                    boolean hasFocus, int row, int column) {
            Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            cell.setForeground(Color.black);

            TestGFRecord record = currentWords.elementAt(row);
            switch(record.checkResult) {
            case TestGFRecord.UNKNOWN:
                cell.setBackground(Color.white);
                if(isSelected)
                    cell.setBackground(SELECTION_COLOR);
                break;
            case TestGFRecord.RIGHT:
                cell.setBackground(RIGHT_COLOR);
                if(isSelected)
                    cell.setBackground(RIGHT_SEL_COLOR);
                break;
            case TestGFRecord.WRONG:
                cell.setBackground(WRONG_COLOR);
                if(isSelected)
                    cell.setBackground(WRONG_SEL_COLOR);
            }
            return cell;
        }
    }
    
    
    /**
     * TestGFWordTableModel class represents table model
     * for words table in TestGapsFillingDialog
     * @author Konstantin Fadeyev
     *
     */
    
    
    private class TestGFWordTableModel extends AbstractTableModel {
    	public TestGFWordTableModel() {
    		super();
    	}

    	public Class<?> getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

    	public int getColumnCount() {
    		return 1;
    	}

    	public String getColumnName(int num) {
    		return "Word";
    	}

        public int getRowCount() {
    	    return shuffledIndices.size();
    	}

    	public Object getValueAt(int row, int column) {
    		if(row < shuffledIndices.size()) {
    			Integer recordIndex = (Integer)shuffledIndices.elementAt(row);
    			TestGFRecord record = (TestGFRecord)currentWords.elementAt(
    					recordIndex.intValue());
    			return record.wordRecord.getWord();
    		} else {
    			return "";
    		}
    	}

        public boolean isCellEditable(int rowIndex, int columnIndex) {
    		return false;
        }
    }
}
